﻿using Castle.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Reflection;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace EMall.Uow
{
    public class UnitOfWorkInterceptor : IUnitOfWorkInterceptor
    {
        private readonly ILogger _logger;
        private readonly IServiceProvider _serviceProvider;
        public UnitOfWorkInterceptor(
            ILogger<UnitOfWorkInterceptor> logger,
            IServiceProvider serviceProvider)
        {
            ArgumentNullException.ThrowIfNull(serviceProvider);
            _logger = logger;
            _serviceProvider = serviceProvider;
        }
        public void Intercept(IInvocation invocation)
        {
            ArgumentNullException.ThrowIfNull(invocation);
            var method = invocation.MethodInvocationTarget ?? invocation.Method;
            if (method.GetCustomAttribute<UnitOfWorkAttribute>(true) is not null)
            {
                var uow = _serviceProvider.GetRequiredService<IUnitOfWork>();
                uow.Begin();
                try
                {
                    try
                    {
                        invocation.Proceed();
                        if (IsAsyncMehod(method))
                        {
                            if (invocation.ReturnValue is Task)
                            {
                                Task.WaitAll((Task)invocation.ReturnValue);
                            }
                        }
                    }
                    catch
                    {
                        uow.Rollback();
                        throw;
                    }
                    uow.Commit();
                }
                catch
                {
                    uow.Rollback();
                    throw;
                }
            }
            else
            {
                invocation.Proceed();
            }
        }
        public static bool IsAsyncMehod(MethodInfo method)
        {
            return method.ReturnType == typeof(Task)
                || (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>));
        }
    }
}